#include "stdafx.h"

#include <cfloat>

#include "FFEXPublicHandler.h"
#include "Service/RequestIDGenerator.h"
#include "Utils/FastBlockQueue.h"
#include "Business/Market.h"
#include "Utils/Logger.h"

#pragma warning(disable:4100)

using namespace std;

FFEXPublicHandler::FFEXPublicHandler(Customer const& customer,
									 ContractCodes const& contractCodes,
									 FastBlockQueue& marketQueue)
:m_pMdApi(0),
m_customer(customer),
m_contractCodes(contractCodes),
m_marketQueue(marketQueue)
{
	for (UINT i = 0; i < m_contractCodes.size(); ++i)
	{
		Variety const& contract = m_contractCodes[i];
		m_contractIndexMap[contract.GetCode()] = i;
	}

}

void FFEXPublicHandler::OnFrontConnected()
{
	CThostFtdcReqUserLoginField reqUserLogin; 
	strcpy_s(reqUserLogin.BrokerID, m_customer.GetID()); 
	strcpy_s(reqUserLogin.UserID, m_customer.GetShanghaiClientID()); 
	strcpy_s(reqUserLogin.Password,m_customer.GetPassword()); 
	m_pMdApi->ReqUserLogin(&reqUserLogin, 1);
}

void FFEXPublicHandler::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, 
										CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (!IsNull(pRspInfo) && pRspInfo->ErrorID != 0)
	{
#ifndef LOG4CPLUS_DISABLE_ERROR
		LightLogger::Error(FT("PublicHandler OnRspUserLogin failed,code[%d]",pRspInfo->ErrorID));
#endif
		return;
	}
	if (!m_marketContractCodes.empty())
	{
		IntelnalSubscribeMarket();
	}
#ifndef LOG4CPLUS_DISABLE_INFO
	LightLogger::Info("PublicHandler OnRspUserLogin success");
#endif
}

void FFEXPublicHandler::OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (IsNull(pRspInfo))
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn("PublicHandlerOnRspError pRspInfo is null");
#endif
		return;
	}

#ifndef LOG4CPLUS_DISABLE_WARN
	LightLogger::Warn(FT("PublicHandler OnRspError code[%d],msg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg));
#endif
}

void FFEXPublicHandler::OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout,
										CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (IsNull(pUserLogout) || IsNull(pRspInfo))
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn("PublicHandler OnRspUserLogout pUserLogout or pRspInfo is null");
#endif
		return;
	}

	if (pRspInfo->ErrorID != 0)
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn(FT("PublicHandler OnRspUserLogout error code[%d], msg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg));
#endif
		return;
	}
#ifndef LOG4CPLUS_DISABLE_INFO
	LightLogger::Info("PublicHandler logout");
#endif
}

void FFEXPublicHandler::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
											CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (IsNull(pSpecificInstrument) || IsNull(pRspInfo))
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn("PublicHandler OnRspSubMarketData pSpecificInstrument or pRspInfo is null");
#endif
		return;
	}

	if (pRspInfo->ErrorID != 0)
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn(FT("PublicHandler OnRspSubMarketData error code[%d], msg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg));
#endif
		return;
	}
#ifndef LOG4CPLUS_DISABLE_INFO
	LightLogger::Info("PublicHandler OnRspSubMarketData success");
#endif
}

void FFEXPublicHandler::OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, 
											  CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (IsNull(pSpecificInstrument) || IsNull(pRspInfo))
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn("PublicHandler OnRspUnSubMarketData pSpecificInstrument or pRspInfo is null");
#endif
		return;
	}

	if (pRspInfo->ErrorID != 0)
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn(FT("PublicHandler OnRspUnSubMarketData error code[%d], msg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg));
#endif
		return;
	}
#ifndef LOG4CPLUS_DISABLE_INFO
	LightLogger::Info("PublicHandler OnRspUnSubMarketData success");
#endif
}
static double Convert2Price(double price)
{
	double p = price;
	if (p == DBL_MAX)
	{
		p = 0;
	}
	return p;
}

static Price Convert2Price(double price, int quantity)
{
	double p = Convert2Price(price);
	return Price(p,quantity);
}

bool IsUnnormalMarket(CThostFtdcDepthMarketDataField* pDepthMarketData)
{
	bool ret = pDepthMarketData->BidPrice1 == DBL_MAX ||
		pDepthMarketData->AskPrice1 == DBL_MAX ||
		pDepthMarketData->UpperLimitPrice == DBL_MAX ||
		pDepthMarketData->LowerLimitPrice == DBL_MAX;
#ifndef LOG4CPLUS_DISABLE_DEBUG
	if (ret)
	{
		LightLogger::Debug(FT("Market[%s] some price is DBL_MAX:BidPrice1[%f],AskPrice1[%f],UpperLimitPrice[%f],LowerLimitPrice[%f]",
			pDepthMarketData->InstrumentID,
			pDepthMarketData->BidPrice1,
			pDepthMarketData->AskPrice1,
			pDepthMarketData->UpperLimitPrice,
			pDepthMarketData->LowerLimitPrice));
	}
#endif
	return ret;
}

void FFEXPublicHandler::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData)
{
	if (IsNull(pDepthMarketData))
	{
#ifndef LOG4CPLUS_DISABLE_WARN
		LightLogger::Warn("OnRtnDepthMarketData pDepthMarketData is null");
#endif		
		return;
	}

	if (pDepthMarketData->Volume == 0)
	{
		return;
	}

	if (IsUnnormalMarket(pDepthMarketData))
	{
		return;
	}

	int contractIndex = GetContractIndex(pDepthMarketData->InstrumentID);
	if (contractIndex < 0)
	{
		return;
	}
	int multiple = m_contractCodes[contractIndex].GetMultiple();

	Market market(CreateMarket(*pDepthMarketData, contractIndex, multiple));

	QueueItem item;
	item.type = QueueItem::MARKET;
	memcpy(item.data,&market,sizeof(market));

 	m_marketQueue.Push(item);
}

void FFEXPublicHandler::OnFrontDisconnected(int nReason)
{
#ifndef LOG4CPLUS_DISABLE_WARN
	LightLogger::Warn("Market disconnected");
#endif
}

void FFEXPublicHandler::OnHeartBeatWarning(int nTimeLapse)
{
#ifndef LOG4CPLUS_DISABLE_WARN
	LightLogger::Warn("Market Beat warning");
#endif
}

void FFEXPublicHandler::UnsubscribeMarket()
{
	char * contracts[400] = {0};
	for (UINT i = 0; i < m_marketContractCodes.size(); ++i)
	{
		contracts[i] = (char*)m_marketContractCodes[i].c_str();
	}

	m_pMdApi->UnSubscribeMarketData(contracts,m_marketContractCodes.size());
}

void FFEXPublicHandler::IntelnalSubscribeMarket()
{
	char* contracts[400] = {0};
	for (UINT i = 0; i < m_marketContractCodes.size(); ++i)
	{
		contracts[i] = (char*)m_marketContractCodes[i].c_str();
	}

	int ret = m_pMdApi->SubscribeMarketData(contracts,m_marketContractCodes.size());
#ifndef LOG4CPLUS_DISABLE_DEBUG
	LightLogger::Debug(FT("SubscribeMarketData return [%d]",ret));
#endif
}

Market FFEXPublicHandler::CreateMarket(CThostFtdcDepthMarketDataField const& depthMarketData, 
									   int contractIndex,
									   int multiple)
{
	Prices buyPrices;
	buyPrices[0] = Convert2Price(depthMarketData.BidPrice1,depthMarketData.BidVolume1);
	buyPrices[1] = Convert2Price(depthMarketData.BidPrice2,depthMarketData.BidVolume2);
	buyPrices[2] = Convert2Price(depthMarketData.BidPrice3,depthMarketData.BidVolume3);
	buyPrices[3] = Convert2Price(depthMarketData.BidPrice4,depthMarketData.BidVolume4);
	buyPrices[4] = Convert2Price(depthMarketData.BidPrice5,depthMarketData.BidVolume5);


	Prices sellPrices;
	sellPrices[0] = Convert2Price(depthMarketData.AskPrice1,depthMarketData.AskVolume1);
	sellPrices[1] = Convert2Price(depthMarketData.AskPrice2,depthMarketData.AskVolume2);
	sellPrices[2] = Convert2Price(depthMarketData.AskPrice3,depthMarketData.AskVolume3);
	sellPrices[3] = Convert2Price(depthMarketData.AskPrice4,depthMarketData.AskVolume4);
	sellPrices[4] = Convert2Price(depthMarketData.AskPrice5,depthMarketData.AskVolume5);

	double upperLimit = Convert2Price(depthMarketData.UpperLimitPrice);
	double lowerLimit = Convert2Price(depthMarketData.LowerLimitPrice);
	Variety dummy_variety("UNDEFINED",-1,-1);
	CTime currentTime = CTime::GetCurrentTime();
	char hour[3] = {depthMarketData.UpdateTime[0],depthMarketData.UpdateTime[1]};
	char minute[3] = {depthMarketData.UpdateTime[3],depthMarketData.UpdateTime[4]};
	char second[3] = {depthMarketData.UpdateTime[6],depthMarketData.UpdateTime[7]};
	CTime time(currentTime.GetYear(),currentTime.GetMonth(),currentTime.GetDay(),
		atoi(hour),atoi(minute),atoi(second));
	Market market(contractIndex,dummy_variety,depthMarketData.LastPrice, depthMarketData.OpenInterest,
				buyPrices,sellPrices,upperLimit,lowerLimit,depthMarketData.PreSettlementPrice,time);
#ifdef DEBUG
	{
		static ofstream os("./market.csv");
		os << depthMarketData.InstrumentID << "," << depthMarketData.LastPrice << ",";
		os << depthMarketData.OpenInterest << "," << depthMarketData.UpdateTime<< "," << endl;
	}
#endif
	return market;
}

int FFEXPublicHandler::GetContractIndex(char const* instrumentID)
{
	map<string,int>::const_iterator iter = m_contractIndexMap.find(instrumentID);
	if (iter == m_contractIndexMap.end())
	{
		// receive not exist contract market,log it and return
#ifndef LOG4CPLUS_DISABLE_DEBUG
		LightLogger::Debug(FT("InstrumentId[%s] can't find contractIndex",instrumentID));
#endif

		return -1;
	}
	return iter->second;
}

void FFEXPublicHandler::SubscribeMarket(char const* contract)
{
	m_marketContractCodes.push_back(contract);
	LightLogger::Info(FT("subscribing [%s] public market",contract));
	IntelnalSubscribeMarket();
}